/**
 * Copyright (C) 2008+ Spark Zheng
 *
 * @file	test/test_tpool.c
 * @brief
 *	      TASK_COMPLEX not defined by default.
 *
 * @history
 *	      version 0.1.0	Spark add initial version for validation test
 *	      version 0.1.2	Spark add performance test with raw NPTL threads
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "threadpool.h"
#include "timer.h"

#define TASK_COMPLEX		1

#define NPTL_NUM		10
#define TP_INIT_NUM		4
#define TP_MAX_NUM		8

#define TP_V_TASKS		2000
#define TP_P_TASKS		500 * 1000

static void *test_v_task(void *targ, void *arg);
static void *test_p_task(void *targ, void *arg);
static void *test_p_thread(void *arg);

typedef struct data {
	int a;
	int b;
} S_DATA;

int main(int argc, char *argv[])
{
	int i, j;
	dlib_tpool_t *tp = NULL;

	if (argc < 2) {
		fprintf(stderr,
			"Usage: test_tpool [p(performance)|v(validation)]\n");
		return -1;
	}

	if (strchr(argv[1], 'v') != NULL) {
		tp = dlib_tpool_init(TP_INIT_NUM, TP_MAX_NUM, NULL, NULL, NULL);
		if (tp == NULL) {
			fprintf(stderr, "dlib_tpool_init error\n");
			return -1;
		}

		for (i = 0; i < TP_V_TASKS; i++) {
			S_DATA *d;
			d = (S_DATA *)malloc(sizeof (*d));
			d->a = i;
			d->b = i * 2;
			dlib_tpool_execute(tp, 2, test_v_task, d);
		}

		dlib_tpool_wait(tp);
		dlib_tpool_dump(tp, NULL);
		dlib_tpool_exit(tp);
	}

	struct timeval start, end, result;

	if (strchr(argv[1], 'p') != NULL) {

		DLIB_UTIMER_RECORD(&start);

		tp = dlib_tpool_init(TP_INIT_NUM, TP_MAX_NUM, NULL, NULL, NULL);
		if (tp == NULL) {
			fprintf(stderr, "dlib_tpool_init error\n");
			return -1;
		}

		for (i = 0; i < TP_P_TASKS; i++) {
			dlib_tpool_execute(tp, 2, test_p_task, NULL);
		}

		dlib_tpool_dump(tp, NULL);
		dlib_tpool_wait(tp);
		dlib_tpool_dump(tp, NULL);
		dlib_tpool_exit(tp);

		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);

		fprintf(stderr, "[p]use tpool: time %06f\n", result.tv_sec +
			(float) result.tv_usec / 1000000);

		pthread_t pid[NPTL_NUM];
		DLIB_UTIMER_RECORD(&start);
		for (j = 0; j < TP_P_TASKS / NPTL_NUM; j++) {
			for (i = 0; i < NPTL_NUM; i++) {
				(void) pthread_create(&pid[i], NULL,
						      test_p_thread, NULL);
			}
			for (i = 0; i < NPTL_NUM; i++) {
				(void) pthread_join(pid[i], NULL);
			}
		}
		DLIB_UTIMER_RECORD(&end);
		DLIB_UTIMER_SUB(&end, &start, &result);

		fprintf(stderr, "[p]use raw NPTL: time %06f\n", result.tv_sec +
			(float) result.tv_usec / 1000000);
	}

	return 0;
}

static int count = 0;
static void *test_v_task(void*targ, void *arg)
{
	UNUSED_PARAM(targ);
	S_DATA *d = (S_DATA *) arg;

	sleep(600);
	fprintf(stderr, "in  test_task : mark is %d, arg a %d, b %d\n",
		count, d->a, d->b);
	count++;
	fflush(stderr);
	free(d);

	return NULL;
}

#if TASK_COMPLEX
static int sum = 0;
static void *test_p_task(void *targ, void *arg)
{
	UNUSED_PARAM(targ);
	UNUSED_PARAM(arg);
	sum += 2;
	sum *= 2;
	sum -= 2;
	sum /= 2;

	return NULL;
}

static void *test_p_thread(void *arg)
{
	UNUSED_PARAM(arg);
	sum += 2;
	sum *= 2;
	sum -= 2;
	sum /= 2;

	pthread_exit(NULL);
}
#else
static void *test_p_task(void *targ, void *arg)
{
	UNUSED_PARAM(targ);
	UNUSED_PARAM(arg);
	usleep(200);
	return NULL;
}

static void *test_p_thread(void *arg)
{
	UNUSED_PARAM(arg);
	usleep(200);
	pthread_exit(NULL);
}
#endif
